﻿/*
 *  Apull
 *  
 *  2021-5-30 v1.0 开始重做C#版
 *  只允许一个实例运行，通过程序名和窗口标题查找重复窗口
 *  
 * 
 * 2022-4-1 v3.2.3 
 * 异步启动程序，防止界面卡死。
 * 添加工作路径，读取快捷方式中的设置。 
 *  
 * 2023-9-10 v3.2.5 
 * 升级到.net 4.8
 * 更改查找重复打开本软件的方法，并关闭提示。
 * 添加拖动滚屏功能
 * 优化获取列表项参数信息方法
 * 列表添加参数和起始目录
 * 
 */

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;


namespace EXEStart
{
    static class Program
    {
        enum Result { Have, noHanve, Error };
        public static string msgTitle;

        /// <summary>
        /// 应用程序的主入口点。
        /// </summary>
        [STAThread]
        public static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(true);

            FrmMain frm = new();

            Version ver = Version.Parse(Application.ProductVersion);
            msgTitle = $"{Application.ProductName}  v{ver.Major}.{ver.Minor}";// +(ver.Build>0?"."+ver.Build:"");

            IntPtr hwnd = IntPtr.Zero;

            if(PrevInstance(System.IO.Path.GetFileNameWithoutExtension(Application.ExecutablePath), ref hwnd, frm.Text) == Result.Have)
            {
                //MessageBox.Show("已经有一个实例在运行！", msgTitle, MessageBoxButtons.OK, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly);

                if(hwnd != IntPtr.Zero)
                {
                    ShowWindow(hwnd, SW_SHOWNORMAL & SW_RESTORE & SW_SHOW);
                    SetForegroundWindow(hwnd);
                }

                frm.Dispose();
                System.GC.Collect();
                Application.Exit();
                return;
            }

            Application.Run(frm);
        }

        /// <summary>
        /// 检测是否有实例在运行
        /// </summary>
        /// <param name="sProName">要检测的实例进程名</param>
        /// <param name="lpWindowName">要检测的窗口标题</param>
        /// <returns>有实例在运行返回true，反之则返回false</returns>
        static private Result PrevInstance(string sProName, ref IntPtr hwnd, string lpWindowName)
        {
            Result returnVal = Result.noHanve;
            List<Process> pro = new List<Process>();

            try
            {
                pro.AddRange(Process.GetProcessesByName(sProName));
                if(pro.Count <= 1)
                {
                    hwnd = FindWindow(null, lpWindowName);
                    if(hwnd != IntPtr.Zero) returnVal = Result.Have;
                }
                else
                {
                    hwnd = pro.Find(x => x.MainWindowHandle != IntPtr.Zero).MainWindowHandle;
                    returnVal = Result.Have;
                }

            }
            catch(Exception ex)
            {
                MessageBox.Show(ex.Message, msgTitle, MessageBoxButtons.OK);
                returnVal = Result.Error;
            }

            return returnVal;
        }


        [DllImport("User32.dll", EntryPoint = "FindWindow")]
        private extern static IntPtr FindWindow(string lpClassName, string lpWindowName);

        /// <summary>
        /// 将指定窗口前置并获得焦点
        /// </summary>
        /// <param name="hWnd">要前置的窗口句柄</param>
        /// <returns>成功返回非零，失败返回0</returns>
        [DllImport("user32.dll")]
        public static extern bool SetForegroundWindow(IntPtr hWnd);


        /// <summary>
        /// 以指定方式显示指定窗口
        /// </summary>
        /// <param name="hWnd">要显示的窗口句柄</param>
        /// <param name="nCmdShow">窗口显示方式</param>
        /// <returns></returns>
        [DllImport("user32.dll")]
        private static extern IntPtr ShowWindow(IntPtr hWnd, int nCmdShow);

        /// <summary>
        /// 窗口显示常量
        /// </summary>
        private const int SW_HIDE = 0;  // 隐藏窗口，活动状态给令一个窗口 
        private const int SW_SHOWNORMAL = 1;
        private const int SW_NORMAL = 1;
        private const int SW_SHOWMINIMIZED = 2;
        private const int SW_SHOWMAXIMIZED = 3;
        private const int SW_MAXIMIZE = 3;
        private const int SW_SHOWNOACTIVATE = 4;  // 用最近的大小和位置显示一个窗口，同时不改变活动窗口
        private const int SW_SHOW = 5; // 用当前的大小和位置显示一个窗口，同时令其进入活动状态
        private const int SW_MINIMIZE = 6; // 最小化窗口，活动状态给令一个窗口
        private const int SW_SHOWMINNOACTIVE = 7; // 最小化一个窗口，同时不改变活动窗口
        private const int SW_SHOWNA = 8; // 用当前的大小和位置显示一个窗口，不改变活动窗口
        private const int SW_RESTORE = 9;  // 与 SW_SHOWNORMAL  1 相同
        private const int SW_SHOWDEFAULT = 10;
        private const int SW_FORCEMINIMIZE = 11;
        private const int SW_MAX = 11;



        // ///////////////////////////////////
        /// <summary>
        /// 显示文件属性API
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        private struct SHELLEXECUTEINFO
        {
            public int cbSize;
            public uint fMask;
            public IntPtr hwnd;
            [MarshalAs(UnmanagedType.LPStr)]
            public string lpVerb;
            [MarshalAs(UnmanagedType.LPStr)]
            public string lpFile;
            [MarshalAs(UnmanagedType.LPStr)]
            public string lpParameters;
            [MarshalAs(UnmanagedType.LPStr)]
            public string lpDirectory;
            public int nShow;
            public IntPtr hInstApp;
            public IntPtr lpIDList;
            [MarshalAs(UnmanagedType.LPStr)]
            public string lpClass;
            public IntPtr hkeyClass;
            public uint dwHotKey;
            public IntPtr hIcon;
            public IntPtr hProcess;
        }

        private const uint SEE_MASK_INVOKEIDLIST = 12;

        [DllImport("shell32.dll")]
        private static extern bool ShellExecuteEx(ref SHELLEXECUTEINFO lpExecInfo);

        /// <summary>
        /// 显示文件属性
        /// </summary>
        /// <param name="Filename">文件名</param>
        public static void ShowFileProperties(string Filename)
        {
            SHELLEXECUTEINFO info = new();
            info.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(info);
            info.lpVerb = "properties";
            info.lpFile = Filename;
            info.nShow = SW_SHOW;
            info.fMask = SEE_MASK_INVOKEIDLIST;
            ShellExecuteEx(ref info);
        }

        // ///////////////////////////////////


        ////////////////////////////////
        //nBar：指定类型的滚动条。0:水平滚动条，1:垂直滚动条。
        /*
        [DllImport("user32.dll")]
        public static extern int SetScrollPos(IntPtr hwnd, int nBar, int nPos, bool bRedraw);

        [DllImport("user32.dll")]
        public static extern int GetScrollPos(IntPtr hwnd, int nBar);

        [DllImport("user32.dll")]
        public static extern bool PostMessage(IntPtr hWnd, int wMsg, int wParam, int lParam);

        [DllImport("user32", CharSet = CharSet.Auto)]
        public static extern bool GetScrollRange(IntPtr hWnd, int nBar, out int lpMinPos, out int lpMaxPos);

        public const int SB_HORZ = 0x0;    // nBar 竖直滚动条
        public const int SB_VERT = 0x1;    // nBar 水平滚动条
        public const int WM_HSCROLL = 0x114;
        public const int WM_VSCROLL = 0x115;
        public const int SB_THUMBPOSITION = 4;
        public const int EM_LINESCROLL = 0xb6;
        */
        //////////////////////


    }

}
